/**
 * @author Jerome Monceaux
 * @version 1.0
 * 
 * Useful methods to convert fundamental type to regular type  
 **/
package org.hh.common;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 
 * @author bilock
 *
 * This class help to introspect a class in order to find 
 * a method that is compatible to a given method signature.
 * It is used to dynamically connect input (method) and output (event to observer) 
 */
@SuppressWarnings("rawtypes")
public class ReflectionUtil {
	
	private static Map<Class,Class> primitiveMap = new HashMap<Class,Class>();
  static {
		primitiveMap.put(double.class, Double.class);
		primitiveMap.put(int.class, Integer.class);
		primitiveMap.put(float.class, Float.class);
		primitiveMap.put(short.class, Short.class);
		primitiveMap.put(long.class, Long.class);
		primitiveMap.put(boolean.class, Boolean.class);
		primitiveMap.put(byte.class, Byte.class);
		primitiveMap.put(char.class, Character.class);
  }

  public static Constructor getCompatibleConstructor(Class c, 
	     Class... paramTypes) {
  	
  	Constructor[] contructors = c.getConstructors();
  	for (int i = 0; i < contructors.length; i++) {
  		Constructor m = contructors[i];

  		Class<?>[] actualTypes = m.getParameterTypes();
  		if( actualTypes == null && paramTypes == null)
  			return m;
  		
  		if ((paramTypes != null && actualTypes !=null) && actualTypes.length != paramTypes.length) {
  			continue;
  		}

  		boolean found = true;
  		for (int j = 0; j < actualTypes.length; j++) {
  			if (!actualTypes[j].isAssignableFrom(paramTypes[j])) { 
  				if (actualTypes[j].isPrimitive()) {
  					found = primitiveMap.get(actualTypes[j]).equals(paramTypes[j]);
  				}
  				else if (paramTypes[j].isPrimitive()) {
  					found = primitiveMap.get(paramTypes[j]).equals(actualTypes[j]);
  				}
  			}
  			
  			if (!found) {
  				break;
  			}
  		}

  		if (found) {
  			return m;
  		}
  	}

  	return null;	
  }
  
	public static Method getCompatibleMethod(Class c, 
				     String methodName, 
				     Class... paramTypes) {
  	
  	Method[] methods = c.getMethods();
  	for (int i = 0; i < methods.length; i++) {
  		Method m = methods[i];

  		if (!m.getName().equals(methodName)) {
  			continue;
  		}

  		Class<?>[] actualTypes = m.getParameterTypes();
  		if( actualTypes == null && paramTypes == null)
  			return m;
  		
  		if ((paramTypes != null && actualTypes !=null) && actualTypes.length != paramTypes.length) {
  			continue;
  		}

  		boolean found = true;
  		for (int j = 0; j < actualTypes.length; j++) {
  			if (!actualTypes[j].isAssignableFrom(paramTypes[j])) { 
  				if (actualTypes[j].isPrimitive()) {
  					found = primitiveMap.get(actualTypes[j]).equals(paramTypes[j]);
  				}
  				else if (paramTypes[j].isPrimitive()) {
  					found = primitiveMap.get(paramTypes[j]).equals(actualTypes[j]);
  				}
  			}
  			
  			if (!found) {
  				break;
  			}
  		}

  		if (found) {
  			return m;
  		}
  	}

  	return null;
  }
}